//
//          Copyright Marshall Clow 2009-2010
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)
//
//

#ifndef	_BOOST_MIME_HPP
#define	_BOOST_MIME_HPP

#include <list>
#include <string>
#include <vector>
#include <iosfwd>

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/include/phoenix.hpp>  // pulls in all of Phoenix
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/adapted/struct.hpp>

#include <boost/shared_ptr.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string.hpp>


// #define	DUMP_MIME_DATA	1


namespace boost { namespace mime {

//	Errors are reported using this exception class
class mime_parsing_error : public std::runtime_error {
public:
    explicit mime_parsing_error ( const std::string & msg ) : std::runtime_error ( msg ) {}
	};

template <class traits> class basic_mime;

namespace detail {

	static const char *k_crlf					= "\015\012";
	static const char *k_package_name			= "Proposed.Boost.Mime";
	static const char *k_package_version		= "0.1";
	static const char *k_content_type_header	= "Content-Type";
	static const char *k_mime_version_header	= "Mime-Version";

	struct default_types {
		typedef	std::string string_type;
	//	typedef std::pair < std::string, string_type > header_type;
		typedef std::vector<char> body_type;
		};


	template<typename string_type>
	struct find_mime_header {
		find_mime_header ( const char *str ) : searchFor ( str ) {}
		bool operator () ( const std::pair<std::string, string_type> &val ) const { return boost::iequals ( val.first, searchFor ); }
	private:
		const char *searchFor;
		};


#ifdef	DUMP_MIME_DATA
	struct tracer {
		tracer ( const char *fn ) : fn_ (fn) { std::cout << "->" << fn_ << std::endl; }
		~tracer () { std::cout << "<-" << fn_ << std::endl; }
		const char *fn_;
		};
#else
	struct tracer {
		tracer ( const char * ) {}
		~tracer () {}
		};
#endif

//	Parsing a Content-Type header
	typedef std::pair<std::string, std::string> phrase_t;
	typedef std::vector < phrase_t > phrase_container_t;
	struct mime_content_type {
		std::string			type;
		std::string 		sub_type;
		phrase_container_t	phrases;
		};
	

	namespace qi  = boost::spirit::qi;
	namespace phx = boost::phoenix;
	using boost::spirit::_val;
	using boost::spirit::_1;
	
	template <typename Iterator, typename Container>
	struct mime_header_parser : qi::grammar<Iterator, Container()>
	{
		mime_header_parser() : mime_header_parser::base_type(mime_headerList)
		{
			mime_headerList = *(mime_header) >> crlf;
			mime_header		= token >> qi::lit ( ':' ) >> value >> crlf;
			token			= qi::char_("a-zA-Z") >> *qi::char_("a-zA-Z_0-9\\-");

		//	In Classifieds/000001, a header begins with a CRLF
			value			= ( valuePart [ _val = _1 ] | qi::eps ) >> *(valueCont [ _val += "\015\012" + _1 ]);
			valueCont		= crlf >> contWS [ _val += _1 ] >> valuePart [ _val += _1 ];
			valuePart		= +qi::char_("\t -~");
			
			contWS			= +qi::char_( " \t");
			crlf			= qi::lit ( k_crlf );

		/*	mime_headerList.name("mime-header-list");
			mime_header.name    ("mime-header");
			token.name          ("mime-token");
			valuePart.name      ("mime-value-part");
			value.name          ("mime-value");

			qi::on_error<qi::fail> ( mime_headerList, 
				std::cout
					<< phoenix::val("Error! Expecting ") 
					<< qi::labels::_4 
					<< phoenix::val(" here: \"") 
					<< phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2) 
					<< phoenix::val("\"")
					<< std::endl
				);
		*/	
		}
		
		qi::rule<Iterator, Container()> mime_headerList ;
		qi::rule<Iterator, typename Container::value_type()> mime_header;
		qi::rule<Iterator, std::string()> token, value, valueCont, valuePart, contWS;
		qi::rule<Iterator> crlf;
	};
	
	template<typename Container, typename Iterator>
	static Container read_headers ( Iterator &begin, Iterator end ) {
		tracer t ( __func__ );
		Container retVal;

		mime_header_parser<Iterator, Container> mh_parser;
		bool b = qi::parse ( begin, end, mh_parser, retVal );
		if ( !b )
			throw mime_parsing_error ( "Failed to parse headers" );
			
#ifdef	DUMP_MIME_DATA
		std::cout             << "******Headers*******" << std::endl;
		for ( typename Container::const_iterator iter = retVal.begin (); iter != retVal.end (); ++iter ) {
			std::string val = iter->second;
			size_t idx;
			while ( std::string::npos != ( idx = val.find ( k_crlf )))
				val.replace ( idx, std::strlen ( k_crlf ), "\n" );
			std::cout << iter->first << ": " << val << std::endl;
			}
		std::cout << std::endl << "******Headers*******" << std::endl;
#endif

		return retVal;
		}

//	The structure of a Content-Type mime header is taken from RFC 2045
//		http://www.ietf.org/rfc/rfc2045.txt, section 5.1

	template <typename Iterator>
	struct mime_content_type_parser : qi::grammar<Iterator, mime_content_type()>
	{
		mime_content_type_parser() : mime_content_type_parser::base_type(content_type_header)
		{
				content_type_header =  *qi::lit(' ') >> part >> '/' >> sub_part >> *phrase ;
	
				part				=  token | extension_token;
				sub_part			=  token | extension_token;
	
				phrase				=  qi::lit ( ';' ) >> +ws >> attribute >> '=' >> value >> *ws;
				ws					=  qi::char_( " \t") | line_sep | comment;
				line_sep			=  qi::lexeme[ qi::lit ( k_crlf ) ];
				
				attribute			= token.alias();
				value				= token | quoted_string;
				
				token				= +(qi::char_( " -~" ) - qi::char_( " ()<>@,;:\\\"/[]?=" ));
				comment				=  qi::lit ('(') >> +(qi::char_(" -~" ) - ')' ) >> qi::lit(')');
				quoted_string		=  qi::lit ('"') >> +(qi::char_(" -~" ) - '"' ) >> qi::lit('"');
				extension_token		=  qi::char_ ( "Xx" ) >> qi::lit ( '-' ) >> token;
		}
		
		qi::rule<Iterator, mime_content_type()> content_type_header ;
		qi::rule<Iterator, phrase_t()> phrase ;
		qi::rule<Iterator, std::string()> part, sub_part, token, attribute, value, quoted_string, extension_token;
		qi::rule<Iterator> ws, line_sep, comment;
	};
	
	template<typename string_type>
	mime_content_type parse_content_type ( const string_type &theHeader ) {
		tracer t ( __func__ );
		mime_content_type retVal;
		typename string_type::const_iterator first = theHeader.begin ();
		mime_content_type_parser<typename string_type::const_iterator> ct_parser;
		bool b = qi::parse ( first, theHeader.end (), ct_parser, retVal );
		if (!b)
			throw mime_parsing_error ( "Failed to parse the 'Content-Type' header" );
		
		return retVal;
		}

	template<typename string_type>
	static string_type get_ct_value ( const string_type &ctString, const char *key ) {
		tracer t ( __func__ );
		mime_content_type mc = parse_content_type ( ctString );
		for ( phrase_container_t::const_iterator iter = mc.phrases.begin (); iter != mc.phrases.end (); ++iter )
			if ( boost::iequals ( iter->first, key ))
				return iter->second;
		
		throw std::runtime_error ( str ( boost::format ( "Couldn't find Content-Type phrase (%s)" ) % key ));
		}
	

	//	Replace this with a spirit thing later.
	//	we're looking for '; boundary="<somevalue>".*'
	std::string get_boundary ( const std::string &ctString ) {
		tracer t ( __func__ );
		return get_ct_value ( ctString, "boundary" );
		}


	//	Read the body of a multipart 
	//	Return a Container of containers, where the first is the actual body,
	//	and the rest are the sub-parts.
	//	Note that the body of the multipart can be empty.
	//	If this is the case, then the first separator need not have a crlf

	//	if the marker is "abcde", we could have:
	//		Note that the separators are really CRLF--abcdeCRLF and CRLF--abcde--CRLF
	//
	//	multipart body
	//	--abcde
	//		sub part #1
	//	--abcde
	//		sub part #2
	//	--abcde--
	//
	//	** or **
	//	In this case, the first separator is --abcdeCRLF
	//
	//	--abcde		(no multipart body!)
	//		sub part #1
	//	--abcde
	//		sub part #2
	//	--abcde--
	
	typedef	std::vector<char>		sub_part_t;
	typedef std::vector<sub_part_t>	sub_parts_t;

	template<typename bodyContainer>
	struct multipart_body_type {
		bool							prolog_is_missing;
		bodyContainer					body_prolog;
		sub_parts_t						sub_parts;
		 bodyContainer					body_epilog;	
		};


	//	Parse a mulitpart body.
	//	Either "--boundaryCRLF" -- in which case the body is empty
	//	or		<some sequence of chars> "CRLF--boundaryCRLF" -- in which case we return the sequence
	//	
	//	I am deliberately not checking for a termination separator here
	template <typename Iterator, typename Container>
	struct multipart_body_parser : qi::grammar<Iterator, Container()> {
		multipart_body_parser( const std::string &boundary, bool &isMissing ) : multipart_body_parser::base_type(mimeBody), m_is_missing ( isMissing ) {
			m_is_missing = false;
		//	Thanks to Michael Caisse for the hint to get this working
			mimeBody	%= bareSep [ phx::ref ( m_is_missing ) = true ] | (+(qi::char_ - sep) >> sep ) ;
			bareSep	    = qi::lit("--") >> boundary >> crlf;
			sep	        = crlf >> bareSep;
			crlf 		= qi::lit ( k_crlf );
			}
	
		bool &m_is_missing;
		qi::rule<Iterator, Container()> mimeBody;
		qi::rule<Iterator> bareSep, sep, crlf;
		};
	
	
	//	Break up a multi-part into its' constituent sub parts.
	template <typename Iterator, typename Container>
	struct multipart_part_parser : qi::grammar<Iterator, Container()> {
		multipart_part_parser( const std::string &boundary ) : multipart_part_parser::base_type(mimeParts) {
			mimeParts	= (+(qi::char_ - sep) % (sep >> crlf)) > terminator ;
			sep	        = crlf >> qi::lit("--") >> boundary ;
			terminator	= sep >> qi::lit("--") >> crlf ;
			crlf 		= qi::lit ( k_crlf );
			}
		qi::rule<Iterator, Container()> mimeParts;
		qi::rule<Iterator> sep, terminator, crlf;
		};


	template<typename Iterator, typename bodyContainer>
	static void read_multipart_body ( Iterator &begin, Iterator end, multipart_body_type<bodyContainer> &mp_body, const std::string &separator ) {
		tracer t ( __func__ );
		typedef bodyContainer innerC;
		innerC mpBody;
		multipart_body_parser <Iterator, innerC> mb_parser (separator, mp_body.prolog_is_missing );
		if ( !qi::parse ( begin, end, mb_parser, mp_body.body_prolog ))
			throw mime_parsing_error ("Failed to parse mime body(1)");
		
		multipart_part_parser <Iterator, sub_parts_t> mp_parser ( separator );
		if ( !qi::parse ( begin, end, mp_parser, mp_body.sub_parts ))
			throw mime_parsing_error ( "Failed to parse mime body(2)");
		std::copy ( begin, end, std::back_inserter ( mp_body.body_epilog ));
		
	#ifdef	DUMP_MIME_DATA
			std::cout << std::endl << ">>****Multipart Body*******" << std::endl;
			std::cout << str ( boost::format ( "Body size %d, sub part count = %d, trailer size = %d %s" ) % mp_body.body_prolog.size () % mp_body.sub_parts.size () % mp_body.body_epilog.size () % ( mp_body.prolog_is_missing ? "(missing)" : "" )) << std::endl;
			std::cout << std::endl << "****** Multipart Body Prolog *******" << std::endl;
			std::copy ( mp_body.body_prolog.begin (), mp_body.body_prolog.end(), std::ostream_iterator<char> ( std::cout ));
			std::cout << std::endl << "****** Multipart Body Epilog *******" << std::endl;
			std::copy ( mp_body.body_epilog.begin (), mp_body.body_epilog.end(), std::ostream_iterator<char> ( std::cout ));
			std::cout << std::endl << "<<****Multipart Body*******" << std::endl;
	#endif
		}


	template<typename Container, typename Iterator>
	static Container read_simplepart_body ( Iterator &begin, Iterator end ) {
		tracer t ( __func__ );
		Container retVal;
		std::copy ( begin, end, std::back_inserter(retVal));
		
#ifdef	DUMP_MIME_DATA
		std::cout << std::endl << ">>****SinglePart Body*******" << std::endl;
		std::cout << str ( boost::format ( "Body size %d" ) % retVal.size ()) << std::endl;
		std::copy ( retVal.begin (), retVal.end(), std::ostream_iterator<char> ( std::cout ));
		std::cout << std::endl << "<<****SinglePart Body*******" << std::endl;
#endif
		return retVal;
		}

//	FIXME: Need to break the headers at 80 chars...
	template<typename headerList>
	void write_headers ( std::ostream &out, const headerList &headers ) {
		if ( headers.size () > 0 ) {
			for ( typename headerList::const_iterator iter = headers.begin (); iter != headers.end (); ++iter )
				out << iter->first << ':' << iter->second << detail::k_crlf;
			}
		out << detail::k_crlf;
		}
	
	template <typename bodyContainer>
	void write_body ( std::ostream &out, const bodyContainer &body ) {
		std::copy ( body.begin (), body.end (), std::ostream_iterator<char> ( out ));
		}
	
	inline void write_boundary ( std::ostream &out, std::string boundary, bool isLast, bool leadingCR = true ) {
		if ( leadingCR )
			out << detail::k_crlf;
		out << "--" << boundary;
		if ( isLast )
			out << "--";
		out << detail::k_crlf;
		}


	template<typename Iterator, typename traits>
	static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end, const char *default_content_type = "text/plain" );
	}


template <class traits = detail::default_types>
class basic_mime {
public:

	typedef enum { simple_part, multi_part, message_part } part_kind;
//	Types for headers
	typedef typename traits::string_type			string_type;
	typedef std::pair< std::string, string_type>	headerEntry;
	typedef std::list<headerEntry> 					headerList;
	typedef typename headerList::iterator			     headerIter;
	typedef typename headerList::const_iterator		constHeaderIter;

//	Types for the parts
	typedef boost::shared_ptr<basic_mime>		mimePtr;
	typedef std::vector<mimePtr>				partList;
	typedef typename partList::iterator				 partIter;
	typedef	typename partList::const_iterator	constPartIter;

//	Type for the body
	typedef	typename traits::body_type			bodyContainer;
	typedef boost::shared_ptr<bodyContainer>	mimeBody;
	
// -----------------------------------------------------------
//	Constructors, destructor, assignment, and swap
// -----------------------------------------------------------

	basic_mime ( const char *type, const char *subtype )
		: m_body_prolog_is_missing ( false ), m_body ( new bodyContainer ), m_body_epilog ( new bodyContainer ) {
		if ( NULL == type || NULL == subtype || 0 == std::strlen ( type ) || 0 == std::strlen ( subtype ))
			throw std::runtime_error ( "Can't create a mime part w/o a type or subtype" );
		
	//	We start with just two headers, "Content-Type:" and "Mime-Version"
	//	Everything else is optional.
		m_part_kind = part_kind_from_string_pair ( type, subtype );
		std::string ctString = str ( boost::format ( "%s/%s" ) % type % subtype );
		set_header_value ( detail::k_content_type_header, ctString );
		set_header_value ( detail::k_mime_version_header, str ( boost::format ( "1.0 (%s %s)" ) % detail::k_package_name % detail::k_package_version ));
		}
	
	basic_mime ( const headerList &theHeaders, const string_type &default_content_type )
		: m_body_prolog_is_missing ( false ), m_body ( new bodyContainer ), m_body_epilog ( new bodyContainer ),
			m_default_content_type ( default_content_type ) {
		string_type ct = m_default_content_type;
	
		constHeaderIter found = std::find_if ( theHeaders.begin (), theHeaders.end (), 
									detail::find_mime_header<string_type> ( detail::k_content_type_header ));
		if ( found != theHeaders.end ())
			ct = found->second;
		
		detail::mime_content_type mct = detail::parse_content_type ( ct );
		m_part_kind = part_kind_from_string_pair ( mct.type, mct.sub_type );
		m_headers = theHeaders;
		}
	
	basic_mime ( const basic_mime &rhs )
			: m_part_kind ( rhs.m_part_kind ), m_headers ( rhs.m_headers ), m_body_prolog_is_missing ( rhs.m_body_prolog_is_missing ), 
		  	m_body ( new bodyContainer ( *rhs.m_body )), m_body_epilog ( new bodyContainer ( *rhs.m_body_epilog )),
		/*	m_subparts ( rhs.m_subparts ), */ m_default_content_type ( rhs.m_default_content_type )
		{
	//	Copy the parts -- not just the shared pointers
		for ( typename partList::const_iterator iter = rhs.subpart_begin (); iter != rhs.subpart_end (); ++iter )
			m_subparts.push_back ( mimePtr ( new basic_mime ( **iter )));
		}

//	Simple, copy constructor-based assignment
//	If this is not efficient enough, then I can optimize it later
	basic_mime & operator = ( const basic_mime &rhs ) {
		basic_mime temp ( rhs );
		this->swap ( temp );
		return *this;
		}	

	void swap ( basic_mime &rhs ) throw () {
		std::swap ( m_part_kind,				rhs.m_part_kind );
		std::swap ( m_headers,					rhs.m_headers );
		std::swap ( m_body_prolog_is_missing,	rhs.m_body_prolog_is_missing );
		std::swap ( m_body,						rhs.m_body );
		std::swap ( m_body_epilog,				rhs.m_body_epilog );
		std::swap ( m_subparts,					rhs.m_subparts );
		std::swap ( m_default_content_type, 	rhs.m_default_content_type );
		}

	~basic_mime () {}
	

//	What kind of part is this (simple, multi, message)
	part_kind get_part_kind () const { return m_part_kind; }

//	Sub-part information
//	FIXME: Need some error checking here
//		No sub-parts for simple parts, for example.
	size_t part_count () const { return m_subparts.size (); }

	boost::shared_ptr <basic_mime> operator [] ( std::size_t idx ) const {
		check_subpart_index ( idx );
		return m_subparts [ idx ];
		}

	void append_part ( boost::shared_ptr<basic_mime> newPart ) {
		check_subpart_append ();
		m_subparts.push_back ( newPart );
		}
	
	partIter		subpart_begin ()		{ return m_subparts.begin (); }
	partIter		subpart_end   ()		{ return m_subparts.end   (); }
	constPartIter	subpart_begin () const	{ return m_subparts.begin (); }
	constPartIter	subpart_end   () const	{ return m_subparts.end   (); }
	
//	Reading the raw headers
	headerIter 		header_begin ()			{ return m_headers.begin (); }
	headerIter 		header_end   ()			{ return m_headers.end   (); }
	constHeaderIter header_begin () const	{ return m_headers.begin (); }
	constHeaderIter header_end   () const	{ return m_headers.end   (); }

// -----------------------------------------------------------
//	Header manipulation
// -----------------------------------------------------------

//	The 'tag' part of the header is still a std::string
	bool header_exists ( const char *key ) const {
		return header_end () != find_header ( key );
		}
		
	string_type header_value  ( const char *key ) const  {
		constHeaderIter found = find_header ( key );
		if ( found == header_end ())
			throw std::runtime_error ( "'header_value' not found" );
		return found->second;
		}
	
	void set_header_value  ( const char *key, const string_type &value, bool replace = false ) {
		if ( !replace )
			m_headers.push_back ( std::make_pair ( std::string ( key ), value ));
		else {
			headerIter found = find_header ( key );
			if ( found == m_headers.end ())
				throw std::runtime_error ( "'header_value' not found - can't replace" );
			found->second = value;
			}
		}

		
	string_type	get_content_type_header () const {
		constHeaderIter found = find_header ( detail::k_content_type_header );
		return found != header_end () ? found->second : m_default_content_type;
		}
	
	
	string_type	get_content_type () const {
		detail::mime_content_type mct = detail::parse_content_type ( get_content_type_header ());
		return string_type ( mct.type ) + '/' + mct.sub_type;
		}
	

//	Special purpose helper routine
	void	append_phrase_to_content_type ( const char *key, const string_type &value ) {
		headerIter found = find_header ( detail::k_content_type_header );
	
	//	Create a Content-Type header if there isn't one
		if ( m_headers.end () == found ) {
			m_headers.push_back ( std::make_pair ( std::string ( detail::k_content_type_header ), m_default_content_type ));
			found = find_header ( detail::k_content_type_header );
			}
	
		detail::mime_content_type mct = detail::parse_content_type ( found->second );
		detail::phrase_container_t::const_iterator p_found =
			std::find_if ( mct.phrases.begin (), mct.phrases.end (), 
				detail::find_mime_header<std::string> ( key ));
		if ( p_found != mct.phrases.end ())
			throw std::runtime_error ( "phrase already exists" );
		found->second += str ( boost::format ( "; %s=\"%s\"" ) % key % value );
	}	
	
	
//	Body get/set methods
	mimeBody body ()		const { return m_body; }
	mimeBody body_prolog ()	const { return m_body; }
	mimeBody body_epilog ()	const { return m_body_epilog; }

	std::size_t body_size () const { return m_body->size (); }

	template <typename Iterator>
	void set_body ( Iterator begin, Iterator end ) {
		bodyContainer temp;
		std::copy ( begin, end, std::back_inserter ( temp ));
		m_body->swap ( temp );
		}

	void set_body ( const char *contents, size_t sz ) { set_body ( contents, contents + sz ); }
	void set_body ( std::istream &in )                { set_body ( std::istream_iterator<char> ( in ), std::istream_iterator<char> ()); }
	void set_body ( const bodyContainer &new_body )   { *m_body = new_body; }

	void set_multipart_prolog_is_missing ( bool isMissing ) { m_body_prolog_is_missing = isMissing; }
	void set_body_prolog ( const bodyContainer &new_body_prolog ) { *m_body        = new_body_prolog; }
	void set_body_epilog ( const bodyContainer &new_body_epilog ) { *m_body_epilog = new_body_epilog; }

// -----------------------------------------------------------
//	Output
// -----------------------------------------------------------
	void stream_out ( std::ostream &out ) {	// called by operator << 
		if ( m_part_kind == simple_part ) {
			detail::write_headers ( out, m_headers );
			detail::write_body ( out, *m_body );
			}
		else if ( m_part_kind == message_part ) {
			if ( m_subparts.size () != 1 )
				throw std::runtime_error ( "message part w/wrong number of sub-parts - should be 1" );
				
			detail::write_headers ( out, m_headers );
			m_subparts [ 0 ]->stream_out ( out );
			}
		else { //	multi-part
		//	Find or invent a boundary string
			std::string boundary;
			try { boundary = detail::get_boundary ( get_content_type_header ()); }
			catch ( std::runtime_error & ) {
			//	FIXME: Make boundary strings (more?) unique
				boundary = str ( boost::format ( "------=_NextPart-%s.%08ld" ) % detail::k_package_name % std::clock ());
				append_phrase_to_content_type ( "boundary", boundary );
				}
			
		//	If the body prolog is missing, we don't want a CRLF on the front of the first sub-part.
		//	Note that there's a (subtle) difference between an zero length body and a missing one.
		//	See the comments in the parser code for more information.
			detail::write_headers ( out, m_headers );
			bool writeCR = body_prolog ()->size () > 0 || !m_body_prolog_is_missing;
			detail::write_body ( out, *body_prolog ());
			for ( typename partList::const_iterator iter = m_subparts.begin (); iter != m_subparts.end (); ++iter ) {
				detail::write_boundary ( out, boundary, false, writeCR );
				(*iter)->stream_out ( out );
				writeCR = true;
				}
			detail::write_boundary ( out, boundary, true );
			detail::write_body ( out, *body_epilog ());
			}
	//	out << detail::k_crlf;
		}
	
//	Build a simple mime part
	template <typename Iterator>
	static basic_mime make_simple_part ( const char *type, const char *subtype, Iterator begin, Iterator end ) {
		basic_mime retval ( type, subtype );
		retval.set_body ( begin, end );
		return retval;
		}

//	Build a mime part from a pair of iterators
	template <typename Iterator>
	static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end ) {
		return detail::parse_mime<Iterator, traits> ( begin, end );
		}

//	Build a mime part from a stream
	static boost::shared_ptr < basic_mime > parse_mime ( std::istream &in ) {
		boost::spirit::istream_iterator first (in);
		boost::spirit::istream_iterator last;
		return parse_mime ( first, last );
		}
		

private:
	basic_mime ();	// Can't create a part w/o a type

	headerIter	find_header ( const char *key ) {
		return std::find_if ( header_begin (), header_end (), detail::find_mime_header<string_type> ( key ));
		}
	
	constHeaderIter find_header ( const char *key ) const {
		return std::find_if ( header_begin (), header_end (), detail::find_mime_header<string_type> ( key ));
		}

	static part_kind part_kind_from_string_pair ( const std::string &type, const std::string &sub_type ) {
		if ( boost::iequals ( type, "multipart" ))
			return multi_part;
			
		part_kind retVal = simple_part;
	//	I expect that this will get more complicated as time goes on....
	//
	//	message/delivery-status is a simple type.
	//	RFC 3464 defines message/delivery-status <http://www.faqs.org/rfcs/rfc3464.html>
	//	The body of a message/delivery-status consists of one or more
	//	   "fields" formatted according to the ABNF of RFC 822 header "fields"
	//	   (see [RFC822]).  
		if ( boost::iequals ( type, "message" ))
			if ( !boost::iequals ( sub_type, "delivery-status" ))
				retVal = message_part;
		return retVal;
		}
	
	void check_subpart_index ( size_t idx ) const {
		if ( get_part_kind () == simple_part )
			throw std::runtime_error ( "Simple Mime parts don't have sub-parts" );
		else if ( get_part_kind == multi_part ) {
				if ( idx >= m_subparts.size ())
					throw std::runtime_error ( 
						str ( boost::format ( "Trying to access part %d (of %d) sub-part to a multipart/xxx mime part" ) % idx %  m_subparts.size ()));
			}
		else {	// message-part
			if ( get_part_kind () == message_part )
				if ( m_subparts.size () > 1 )
					throw std::runtime_error ( "How did a message/xxx mime parts get more than one sub-part?" );

			if ( idx >= m_subparts.size ())
				throw std::runtime_error ( 
					str ( boost::format ( "Trying to access part %d (of %d) sub-part to a message/xxx mime part" ) % idx %  m_subparts.size ()));
			}
		}

	void check_subpart_append () const {
		if ( get_part_kind () == simple_part )
			throw std::runtime_error ( "Simple Mime parts don't have sub-parts" );
		else if ( get_part_kind () == message_part ) {
			if ( m_subparts.size () > 0 )
				throw std::runtime_error ( "Can't add a second sub-part to a message/xxx mime part" );
			}
	//	else { /* Multi-part */ }	// We can always add to a multi-part
		}
	
	part_kind	m_part_kind;
	headerList	m_headers;
	bool		m_body_prolog_is_missing;	// only for multiparts
	mimeBody	m_body;
	mimeBody	m_body_epilog;				// only for multiparts
	partList	m_subparts;					// only for multiparts or message
	string_type	m_default_content_type;
	};


namespace detail {

	template<typename Iterator, typename traits>
	static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end, const char *default_content_type ) {
		tracer t ( __func__ );
		typedef	typename boost::mime::basic_mime<traits>	mime_part;
		
		shared_ptr < mime_part > retVal (
			new mime_part ( detail::read_headers<typename mime_part::headerList> ( begin, end ), default_content_type ));
	
		std::string content_type = retVal->get_content_type ();

#ifdef	DUMP_MIME_DATA
		std::cout << "Content-Type: " << content_type << std::endl;
		std::cout << str ( boost::format ( "retVal->get_part_kind () = %d" ) % ((int) retVal->get_part_kind ())) << std::endl;
#endif

		if ( retVal->get_part_kind () == mime_part::simple_part )
			retVal->set_body ( detail::read_simplepart_body<typename mime_part::bodyContainer, Iterator> ( begin, end ));
		else if ( retVal->get_part_kind () == mime_part::message_part ) {
		//	If we've got a message/xxxx, then there is no body, and we have a single
		//	embedded mime_part (which, of course, could be a multipart)
			retVal->append_part ( parse_mime<Iterator, traits> ( begin, end ));
			}
		else /* multi_part */ {
		//	Find or invent a boundary string
			std::string part_separator = detail::get_boundary ( retVal->get_content_type_header ());
			const char *cont_type = boost::iequals ( content_type, "multipart/digest" ) ? "message/rfc822" : "text/plain";
			
			detail::multipart_body_type<typename traits::body_type> body_and_subParts;
			detail::read_multipart_body ( begin, end, body_and_subParts, part_separator );

			retVal->set_body_prolog ( body_and_subParts.body_prolog );
			retVal->set_multipart_prolog_is_missing ( body_and_subParts.prolog_is_missing );
			for ( typename sub_parts_t::const_iterator iter = body_and_subParts.sub_parts.begin ();
					iter != body_and_subParts.sub_parts.end (); ++iter ) {
				typedef typename sub_part_t::const_iterator iter_type;
				iter_type b = iter->begin ();
				iter_type e = iter->end ();
				retVal->append_part ( parse_mime<iter_type, traits> ( b, e, cont_type ));
				}
			retVal->set_body_epilog ( body_and_subParts.body_epilog );
			}
		
		return retVal;
		}	
	
	}


// -----------------------------------------------------------
//
//	Streaming
//
// -----------------------------------------------------------

template<typename traits>
inline std::ostream & operator << ( std::ostream &stream, basic_mime<traits> &part ) {
	part.stream_out ( stream );
	return stream;
	}

template<typename traits>
inline std::ostream & operator << ( std::ostream &stream, boost::shared_ptr <basic_mime<traits> > part ) {
	return stream << *part;
	}


		
}}

BOOST_FUSION_ADAPT_STRUCT(
   boost::mime::detail::mime_content_type,
   (std::string, type)
   (std::string, sub_type)
   (boost::mime::detail::phrase_container_t, phrases)
)

#endif	// _BOOST_MIME_HPP
